package com.zyk.scaffold.tcp.session;

import com.zyk.scaffold.tcp.connector.TcpConnectionManager;
import com.zyk.scaffold.tcp.enums.EventTypeEnum;
import com.zyk.scaffold.tcp.enums.HeartBeatTypeEnum;
import com.zyk.scaffold.tcp.listener.HeartbeatListener;
import com.zyk.scaffold.tcp.listener.SessionListener;
import com.zyk.scaffold.tcp.listener.event.SessionEvent;
import com.zyk.scaffold.tcp.propertises.TcpProperties;
import io.netty.channel.ChannelHandlerContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

@Slf4j
@Component
public class SessionManager implements SessionListener{

    @Autowired
    private TcpConnectionManager tcpConnectionManager;

    List<SessionListener> sessionListeners = new ArrayList<>();
    SessionConfig sessionConfig = new SessionConfig();
    protected SessionStore sessionStore = new SessionStore();

    public SessionManager(TcpProperties tcpProperties) {
        if(HeartBeatTypeEnum.REQUEST.equals(HeartBeatTypeEnum.converter(tcpProperties.getServer().getHeartBeatType()))){
            HeartbeatListener heartbeatListener = new HeartbeatListener(this);
            Thread heartbeatThread = new Thread(heartbeatListener, "heartbeatListener");
            heartbeatThread.setDaemon(true);
            heartbeatThread.start();
            registSessionListener(heartbeatListener);
        }
    }

    public boolean registSessionListener(SessionListener sessionListener){
        return sessionListeners.add(sessionListener);
    }

    public synchronized Session create(String sessionId, ChannelHandlerContext ctx) {
        Session session = sessionStore.getSession(sessionId);
        if (session != null) {
            log.info("session " + sessionId + " exist!");
            // 如果session已经存在则销毁session
            session.close();
            log.info("session " + sessionId + " have been closed!");
        }
        log.info("create new session " + sessionId + ", ctx -> " + ctx.toString());
        session = new Session(this, sessionConfig, tcpConnectionManager);
        session.setId(sessionId);
        session.setEnable(true);
        session.setCreatedTime(LocalDateTime.now());
        session.setLastAccessedTime(LocalDateTime.now());
        session.setConnection(tcpConnectionManager.created(ctx, session));
        log.info("create new session " + sessionId + " successful!");
        log.debug("add listeners to session " + sessionId + " successful! " + sessionListeners);
        sessionStore.addSession(session);
        sessionCreated(new SessionEvent(EventTypeEnum.SESSION_CREATED, session));
        return session;
    }

    public SessionConfig getSessionConfig() {
        return sessionConfig;
    }
    @Override
    public void onSessionEvent(SessionEvent event){
        sessionListeners.stream().forEach(listener -> listener.onSessionEvent(event));
    }

    @Override
    public void sessionCreated(SessionEvent event){
        sessionListeners.stream().forEach(listener -> listener.sessionCreated(event));
    }

    @Override
    public void sessionDestroyed(SessionEvent event){
        sessionListeners.stream().forEach(listener -> listener.sessionDestroyed(event));
    }

    public void addSession(Session session) {
        sessionStore.addSession(session);
    }

    public void updateSession(String sessionId) {
        sessionStore.updateSession(sessionId);
    }

    public void removeSession(Session session) {
        sessionStore.removeSession(session);
    }

    public void removeSession(String sessionId) {
        sessionStore.removeSession(sessionId);
    }

    public Session getSession(String sessionId) {
        return sessionStore.getSession(sessionId);
    }

    public Session[] getSessions() {
        return sessionStore.getSessions();
    }

    public Set<String> getSessionKeys() {
        return sessionStore.getSessionKeys();
    }

    public int getSessionCount() {
        return sessionStore.getSessionCount();
    }

    public void refreshHeartbeat(String sessionId, ChannelHandlerContext ctx) {
        sessionStore.refreshHeartbeat(sessionId, ctx);
    }
}
